/*
 * EventSelectServer.cpp
 *
 *  Created on: 2013. 7. 23.
 *      Author: ncanis
 */
#include "Stdafx.h"
#include "EventSelectServer.h"

namespace TED {

EventSelectServer::EventSelectServer(SocketHandler* handler, const TCHAR* ip, const int port) : AbstractEventSelect(handler, ip, port) {
	this->socket = NULL;
}

EventSelectServer::~EventSelectServer() {

}

bool EventSelectServer::Bind() {
	if(socket!=NULL) {
		Logger::Get().ErrorSocket(_T("서버가 이미 구동중 입니다."));
		return false;
	}

	bool success = false;
	socket = Init();

	address.sin_family = AF_INET;		// 주소체계(ipv4) 로설정, PF_INET : 프로토콜 체계
	address.sin_addr.s_addr = Util::ted_inet_addr(ip);		//  long 형 데이터의 바이트 오더를 바꾸어주는 함수, INADDR_ANY=localhost
	address.sin_port = htons(port);		// Host 시스템에서 Network로 short 형 데이터를 보낼 때  바이트 오더를 바꾸어주는 함수

	Logger::Get().Debug(_T("Try to bind=%s:%d"), ip, port);


	// 주소와 Port 할당 (바인드!!)
	// bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
	int result = bind(socket, (struct sockaddr *) &address, sizeof(address));
	if (result == SOCKET_ERROR) {
		closesocket(socket);
		return false;
	}

	// Set the socket to listen
	result = listen(socket, SOMAXCONN);
	if (result == SOCKET_ERROR) {
		closesocket (socket);
		return false;
	}

	// non-blocking 으로 설정한다.
	result = SetBlocking(socket, false); 
	if(result== SOCKET_ERROR) {
		closesocket(socket);
		Logger::Get().ErrorSocket(_T("Cannot set non-blocking mode."));
		return NULL;
	}


	// 연결 성공
	Logger::Get().Debug(_T("Binded Server. server=%s:%d"),ip, port);

	// 연결 이벤트 구동
	IoSession& serverSession = MakeSession(socket);
	result = CreateEvent(&serverSession, FD_ACCEPT | FD_CLOSE);
	if(result==SOCKET_ERROR) {
		closesocket(socket);
		delete &serverSession;
		Logger::Get().ErrorSocket(_T("Cannot create event."));
		return NULL;
	}

	// 이벤트 수신 스레드 시작(Lazy Loading. 오로지 한번만 구동된다.)
	StartEventRecvThread();

	return success;
}


// Event Parsing.
int EventSelectServer::SelectEvent(int index) {
	/*EventSelectInfo info = sessions.at(index);
	WSAEVENT event = info.event;
	IoSession* session = info.session;*/
	WSANETWORKEVENTS netEvents;
	SOCKET clientSocket=0;
	WSAEVENT event=0;
	/**
	 * 주어진 Object가 signal 된 상태가된 원인을 확인한다.
	 * 1. 이벤트가 발생한 소켓의 핸들
	 * 2. 소켓과 연결된 signaled 상태인 Event 오브젝트의 핸들
	 * 3. 발생한 이벤트의 유형정보와 오류정보로 채워질 WSANETWORKEVENTS 구조체 변수의 주소값
	 */
	int result = WSAEnumNetworkEvents(clientSocket, event, &netEvents);
	if(result == SOCKET_ERROR)
	{
		return 0;
	}

	//  연결 요청 수신
	if (netEvents.lNetworkEvents & FD_ACCEPT) {
		if (netEvents.iErrorCode[FD_ACCEPT_BIT] != 0) {
			puts("Accept Error");
			return 0;
		}
		Accept();
	}
	// 데이터 수신
	if (netEvents.lNetworkEvents & FD_READ) {
		if (netEvents.iErrorCode[FD_READ_BIT] != 0) {
			printf("Read Error");
			return 0;
		}
		EventSelectInfo info = sessions.at(index);
		IoSession* session = info.session;
		Recv(*session);
	}

	// 연결 종료 요청
	if (netEvents.lNetworkEvents & FD_CLOSE) {
		if (netEvents.iErrorCode[FD_CLOSE_BIT] != 0) {
			puts("Close Error");
			return 0;
		}
		EventSelectInfo info = sessions.at(index);
		IoSession* session = info.session;
		//  manual-reset 모드 event 오브젝트의 삭제
		Close(*session);
	}
}


void EventSelectServer::Accept() {
	SOCKADDR_IN clientAddress;
	INT clientLength = sizeof(clientAddress);

	// 연결을 수락
	// (accept | 성공시 소켓핸들 실패시 "INVALID_SOCKET" 반환)
	SOCKET clientSocket = accept(socket, (SOCKADDR*) &clientAddress, &clientLength);

	// 연결 이벤트 구동
	IoSession& clientSession = MakeSession(clientSocket);
	int result = CreateEvent(&clientSession, FD_READ | FD_CLOSE);
	if(result==SOCKET_ERROR) {
		closesocket(socket);
		delete &clientSession;
		Logger::Get().ErrorSocket(_T("Cannot create event."));
		return;
	}

	handler->Open(clientSession);
}


//// Bind를 Dispose 한다.
//bool EventSelectServer::Dispose() {
//	// socket, 더이상 허락하지 않는 명령어(SD_RECEIVE, SD_SEND, SD_BOTH)
//	bool success = false;
//	INT result = shutdown(socket, SD_SEND);
//	if (result == SOCKET_ERROR) {
////			Log("Fail to close socket.");
//	} else {
//		success = true;
//	}
//	// 할당 받은 리소스 반환.
//	// WS2_32.DLL의 사용을 종료하는 함수
//	WSACleanup();
//	return success;
//}

}